#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include "config.h"
#include "macros.h"
#include "debug.h"
#include "applies.h" // for APPLY___INIT
#include "simulate.h" // for strput, EndOf
#include "scratchpad.h"
#include "stralloc.h"
#include "qsort.h"
#include "icode.h"
#include "compiler.h"

#define align(x) (((x) + 3) & ~3)

int current_line;
char *current_file;

#define CT(x) (1 << (x))
#define CT_SIMPLE(x) (CT(TYPE_ANY) | CT(x))

short compatible[] = {
	/* ANY */      0xfff,
	/* NOVALUE to*/CT_SIMPLE(TYPE_NOVALUE) | CT(TYPE_VOID) | CT(TYPE_NUMBER),
	/* VOID to*/   CT_SIMPLE(TYPE_VOID) | CT(TYPE_NUMBER),
	/* NUMBER to*/ CT_SIMPLE(TYPE_NUMBER) | CT(TYPE_REAL),
	/* STRING */   CT_SIMPLE(TYPE_STRING),
	/* OBJECT */   CT_SIMPLE(TYPE_OBJECT),
	/* MAPPING */  CT_SIMPLE(TYPE_MAPPING),
	/* REAL */     CT_SIMPLE(TYPE_REAL) | CT(TYPE_NUMBER),
};

short is_type[] = {
	/* ANY */     0xfff,
	/* NOVALUE */ CT_SIMPLE(TYPE_NOVALUE) | CT(TYPE_VOID),
	/* VOID */    CT_SIMPLE(TYPE_VOID) | CT(TYPE_NOVALUE),
	/* NUMBER */  CT_SIMPLE(TYPE_NUMBER),
	/* STRING */  CT_SIMPLE(TYPE_STRING),
	/* OBJECT */  CT_SIMPLE(TYPE_OBJECT),
	/* MAPPING */ CT_SIMPLE(TYPE_MAPPING),
	/* REAL */    CT_SIMPLE(TYPE_REAL),
};

#define START_BLOCK_SIZE	4096

mem_block_t mem_block[NUMAREAS];

parse_node_t *comp_trees[NUMTREES];

/* the return type of function */
int exact_types;

static int var_defined;

unsigned short *comp_def_index_map, *func_index_map;
static unsigned short *prog_flags, *comp_sorted_funcs;

char *prog_code;
char *prog_code_max;

static program_t NULL_program;

program_t *prog;

static short string_idx[0x100];
static short freed_string;
unsigned char string_tags[0x20];

local_info_t *locals, *locals_ptr;
unsigned short *type_of_locals, *type_of_locals_ptr;

int locals_size = 0;
int type_of_locals_size = 0;
int current_number_of_locals = 0;
int max_num_locals = 0;

/* If more than this is needed, the code needs help :-) */
#define MAX_FUNCTION_DEPTH 10

int num_parse_warning;
int num_parse_error;

void init_locals()
{
	type_of_locals = CALLOCATE(CFG_MAX_LOCAL_VARIABLES, unsigned short);
	locals = CALLOCATE(CFG_MAX_LOCAL_VARIABLES, local_info_t);
	type_of_locals_ptr = type_of_locals;
	locals_ptr = locals;
	locals_size = type_of_locals_size = CFG_MAX_LOCAL_VARIABLES;
	current_number_of_locals = max_num_locals = 0;
}

void free_all_local_names(int flag)
{
	int i;

	for (i=0; i < current_number_of_locals; i++) {
		if (flag && (type_of_locals_ptr[locals_ptr[i].runtime_index] & LOCAL_MOD_UNUSED)) {
			char buf[256];
			char *end = EndOf(buf);
			char *p;

			p = strput(buf, end, "Unused local variable '");
			p = strput(p, end, locals_ptr[i].ihe->name);
			p = strput(p, end, "'");
			yywarn(buf);
		}
		locals_ptr[i].ihe->sem_value--;
		locals_ptr[i].ihe->dn.local_num = -1;
	}
	current_number_of_locals = 0;
	max_num_locals = 0;
}

void deactivate_current_locals()
{
	int i;

	for (i = 0; i < current_number_of_locals; i++)
		locals_ptr[i].ihe->dn.local_num = -1;
}

void reactivate_current_locals()
{
	int i;

	for (i = 0; i < current_number_of_locals; i++) {
		locals_ptr[i].ihe->dn.local_num = locals_ptr[i].runtime_index;
		locals_ptr[i].ihe->sem_value++;
	}
}

void clean_up_locals()
{
	int offset;

	offset = locals_ptr + current_number_of_locals - locals;
	while (offset--) {
		locals[offset].ihe->sem_value--;
		locals[offset].ihe->dn.local_num = -1;
	}
	current_number_of_locals = 0;
	max_num_locals = 0;
	locals_ptr = locals;
	type_of_locals_ptr = type_of_locals;
}

void pop_n_locals(int num) {
	int lcur_start;
	int ltype_start, i1;

	DEBUG_CHECK(num < 0, "pop_n_locals called with num < 0");
	if (num == 0)
		return;

	lcur_start = current_number_of_locals -= num;
	ltype_start = locals_ptr[lcur_start].runtime_index;

	i1 = num;
	while (i1--) {
		if (type_of_locals_ptr[ltype_start] & LOCAL_MOD_UNUSED) {
			char buf[256];
			char *end = EndOf(buf);
			char *p;

			p = strput(buf, end, "Unused local variable '");
			p = strput(p, end, locals_ptr[lcur_start].ihe->name);
			p = strput(p, end, "'");
			yywarn(buf);
		}
		locals_ptr[lcur_start].ihe->sem_value--;
		locals_ptr[lcur_start].ihe->dn.local_num = -1;
		++lcur_start;
		++ltype_start;
	}
}

int add_local_name(char *str, int type)
{
	if (max_num_locals == CFG_MAX_LOCAL_VARIABLES) {
		yyerror("Too many local variables");
		return 0;
	} else {
		ident_hash_elem_t *ihe;

		ihe = find_or_add_ident(str, FOA_NEEDS_MALLOC);
		type_of_locals_ptr[max_num_locals] = type;
		locals_ptr[current_number_of_locals].ihe = ihe;
		locals_ptr[current_number_of_locals++].runtime_index = max_num_locals;
		if (ihe->dn.local_num == -1)
			ihe->sem_value++;
		return ihe->dn.local_num = max_num_locals++;
	}
}

void reallocate_locals()
{
	int offset;
	offset = type_of_locals_ptr - type_of_locals;
	type_of_locals = RESIZE(type_of_locals, type_of_locals_size += CFG_MAX_LOCAL_VARIABLES,
			unsigned short);
	type_of_locals_ptr = type_of_locals + offset;
	offset = locals_ptr - locals;
	locals = RESIZE(locals, locals_size, local_info_t);
	locals_ptr = locals + offset;
}


/*
 * Compare two types, and return true if they are compatible.
 */

/* This one really is t1->t2; it isn't symmetric, since int->void isn't allowed. */
int compatible_types(int t1, int t2)
{
	if (t1 == TYPE_ANY || t2 == TYPE_ANY) return 1;
	if ((t1 == (TYPE_ANY | TYPE_MOD_ARRAY) && (t2 & TYPE_MOD_ARRAY)))
		return 1;
	if ((t2 == (TYPE_ANY | TYPE_MOD_ARRAY) && (t1 & TYPE_MOD_ARRAY)))
		return 1;
	if (t1 & TYPE_MOD_ARRAY) {
		if (!(t2 & TYPE_MOD_ARRAY)) return 0;
		return t1 == (TYPE_MOD_ARRAY | TYPE_ANY) ||
			t2 == (TYPE_MOD_ARRAY | TYPE_ANY) || (t1 == t2);
	} else if (t2 & TYPE_MOD_ARRAY)
		return 0;
	return compatible[t1] & (1 << t2);
}

/* This one is symmetric.  Used for comparison operators, etc */
int compatible_types2(int t1, int t2)
{
	if (t1 == TYPE_ANY || t2 == TYPE_ANY) return 1;
	if ((t1 == (TYPE_ANY | TYPE_MOD_ARRAY) && (t2 & TYPE_MOD_ARRAY)))
		return 1;
	if ((t2 == (TYPE_ANY | TYPE_MOD_ARRAY) && (t1 & TYPE_MOD_ARRAY)))
		return 1;
	if (t1 & TYPE_MOD_ARRAY) {
		if (!(t2 & TYPE_MOD_ARRAY)) return 0;
		return t1 == (TYPE_MOD_ARRAY | TYPE_ANY) ||
			t2 == (TYPE_MOD_ARRAY | TYPE_ANY) || (t1 == t2);
	} else if (t2 & TYPE_MOD_ARRAY)
		return 0;
	if (compatible[t1] & (1 << t2))
		return 1;
	return compatible[t2] & (1 << t1);
}

static char *compiler_type_names[] = {
	"mixed",
	"void",
	"void",
	"int",
	"string",
	"object",
	"mapping",
	"float",
};

char *get_type_name(char *where, char *end, int type)
{
	int pointer = 0;

	if (type & TYPE_MOD_ARRAY) {
		pointer = 1;
		type &= ~TYPE_MOD_ARRAY;
	}
	DEBUG_CHECK(type >= sizeof compiler_type_names / sizeof compiler_type_names[0], "Bad type\n");
	where = strput(where, end, compiler_type_names[type]);
	where = strput(where, end, " ");
	if (pointer)
		where = strput(where, end, "* ");
	return where;
}

void type_error(char *str, int type)
{
	static char buff[256];
	char *end = EndOf(buff);
	char *p;

	p = strput(buff, end, str);
	p = strput(p, end, ": \"");
	p = get_type_name(p, end, type);
	p = strput(p, end, "\"");
	yyerror(buff);
}

char *get_two_types(char *where, char *end, int type1, int type2)
{
	where = strput(where, end, "( ");
	where = get_type_name(where, end, type1);
	where = strput(where, end, "vs ");
	where = get_type_name(where, end, type2);
	where = strput(where, end, ")");

	return where;
}

/* TODO: SUPPRESS_COMPILER_INLINES */
char *allocate_in_mem_block(int n, int size)
{
	mem_block_t *mbp = &mem_block[n];
	char *ret;

	if (mbp->current_size + size > mbp->max_size) {
		do {
			mbp->max_size <<= 1;
		} while (mbp->current_size + size > mbp->max_size);

		mbp->block = realloc(mbp->block, mbp->max_size);
	}
	ret = mbp->block + mbp->current_size;
	mbp->current_size += size;
	return ret;
}

static int add_new_function_entry()
{
	int index = mem_block[A_FUNCTION_DEFS].current_size / sizeof(compiler_temp_t);
	allocate_in_mem_block(A_FUNCTION_DEFS, sizeof(compiler_temp_t));

	return index;
}

/*
 * Define a new function. Note that this function is called at least twice
 * for all function definitions. First as a prototype, then as the real
 * function.
 */
/* Warning: returns an index into A_FUNCTIONS, not the full
 * function list
 */
int define_new_function(char *name, int num_arg, int num_local,
		int flags, int type)
{
	int index, num;
	ident_hash_elem_t *ihe;
	function_t *funp = 0;

	index = (ihe = lookup_ident(name)) ? ihe->dn.function_num : -1;
	if (index >= 0) {
		int funflags = FUNCTION_FLAGS(index);

		if (!(funflags & FUNC_PROTOTYPE)
				&& !(flags & FUNC_PROTOTYPE)) {
			char buff[256];
			char *end = EndOf(buff);
			char *p;

			p = strput(buff, end, "Redeclaration of function ");
			p = strput(p, end, name);
			p = strput(p, end, ".");
			yyerror(buff);
			return -1;
		}

		funp = FUNCTION_DEF(index);

		// check argument number
		if (funp->num_arg != num_arg) {
			yywarn("Number of arguments disagrees with previous definition.");
		}

		// check return type
		if (type != funp->type) {
			yywarn("Return type disagrees with previous definition.");
		}

		/* TODO: check arg types here */

		/* If it was yet another prototype, then simply return. */
		if ((flags & FUNC_PROTOTYPE) && !(funflags & FUNC_PROTOTYPE))
			return -1; /* unused for prototypes */

		num = FUNCTION_TEMP(index)->u.index;

		funp->num_local = num_local;
	} else {
		unsigned short argument_start_index;
		compiler_temp_t *newfunc;

		num = mem_block[A_FUNCTIONS].current_size / sizeof(function_t);
		funp = (function_t *)allocate_in_mem_block(A_FUNCTIONS, sizeof(function_t));
		funp->name = make_shared_string(name);
		argument_start_index = INDEX_START_NONE;
		add_to_mem_block(A_ARGUMENT_INDEX, (char *) &argument_start_index,
				sizeof argument_start_index);

		index = add_new_function_entry();
		ihe = find_or_add_ident(funp->name, FOA_GLOBAL_SCOPE);
		ihe->sem_value++;
		ihe->dn.function_num = index;
		newfunc = FUNCTION_TEMP(index);

		newfunc->u.index = num;
		newfunc->flags = flags;

		funp->num_local = num_local;
		funp->num_arg = num_arg;
		funp->type = type;
		funp->address = 0;
	}

	if (num_arg) {
		if (!(flags & FUNC_PROTOTYPE)) {
			int i;

			for (i = 0; i < current_number_of_locals; i++) {
				if (type_of_locals_ptr[locals_ptr[i].runtime_index] & LOCAL_MOD_UNUSED) {
					char buf[256];
					char *end = EndOf(buf);
					char *p;

					p = strput(buf, end, "Unused local variable '");
					p = strput(p, end, locals_ptr[i].ihe->name);
					p = strput(p, end, "'");
					yywarn(buf);
					type_of_locals_ptr[locals_ptr[i].runtime_index] &= ~LOCAL_MOD_UNUSED;
				}
			}
		}

		*((unsigned short *)mem_block[A_ARGUMENT_INDEX].block + num) =
			mem_block[A_ARGUMENT_TYPES].current_size / sizeof(unsigned short);
		add_to_mem_block(A_ARGUMENT_TYPES, (char *)type_of_locals_ptr,
				num_arg * sizeof(*type_of_locals_ptr));

		if (flags & FUNC_PROTOTYPE) {
			int i;

			for (i = 0; i < num_arg; i++) {
				if (*locals_ptr[i].ihe->name)
					type_of_locals_ptr[i] |= LOCAL_MOD_UNUSED;
			}
		}
	}
	return index;
}

static int define_variable(char *name, int type)
{
	variable_t *dummy;
	int n;
	ident_hash_elem_t *ihe;

	n = (mem_block[A_VAR_TEMP].current_size / sizeof(variable_t));

	ihe = find_or_add_ident(name, FOA_GLOBAL_SCOPE);
	if (ihe->dn.global_num == -1) {
		ihe->sem_value++;
		ihe->dn.global_num = n;

		if (n >= 256) {
			yyerror("Too many global variables");
		}
	} else {
		char buf[256];
		char *end = EndOf(buf);
		char *p;

		p = strput(buf, end, "Redeclaration of global variable '");
		p = strput(p, end, ihe->name);
		p = strput(p, end, "'.");
		yywarn(buf);
	}

	dummy = (variable_t *)allocate_in_mem_block(A_VAR_TEMP, sizeof(variable_t));
	dummy->name = name;
	dummy->type = type;

	return n;
}

int define_new_variable(char *name, int type)
{
	int n;
	unsigned short *tp;
	char **np;

	var_defined = 1;
	name = make_shared_string(name);
	n = define_variable(name, type);
	np = (char **)allocate_in_mem_block(A_VAR_NAME, sizeof(char*));
	*np = name;
	tp = (unsigned short *)allocate_in_mem_block(A_VAR_TYPE, sizeof(unsigned short));
	*tp = type;

	return n;
}

#define STRING_HASH(var,str) \
	var = (long)str ^ (long)str >> 16; \
var = (var ^ var >> 8) & 0xff;

short store_prog_string(char *str)
{
	short i, next, *next_tab, *idxp;
	char **p;
	unsigned char hash, mask, *tagp;

	str = make_shared_string(str);
	STRING_HASH(hash, str);
	idxp = &string_idx[hash];

	/* string_tags is a big bit-array, so find correct bit */
	mask = 1 << (hash & 7);
	tagp = &string_tags[hash >> 3];

	p = (char **)&PROG_STRING(0);
	next_tab = (short *) mem_block[A_STRING_NEXT].block;

	if (*tagp & mask) {
		/* search hash chain to see if it's there */
		for (i = *idxp; i >= 0; i = next_tab[i]) {
			if (p[i] == str) {
				free_string(str);	/* needed as string is only free'ed
							 * once. */
				((short *) mem_block[A_STRING_REFS].block)[i]++;
				return i;
			}
		}
		next = *idxp;
	} else {
		*tagp |= mask;
		next = -1;
	}

	/*
	 * New string, add to table
	 */

	if (freed_string >= 0) {
		/* reuse freed string */
		int top;

		i = freed_string;

		top = mem_block[A_STRINGS].current_size / sizeof str;
		for (freed_string++; freed_string < top; freed_string++) {
			if (p[freed_string] == 0)
				break;
		}
		if (freed_string >= top)
			freed_string = -1;
	} else {
		/* grow by one element. */
		allocate_in_mem_block(A_STRINGS, sizeof str);
		allocate_in_mem_block(A_STRING_NEXT, sizeof(short));
		allocate_in_mem_block(A_STRING_REFS, sizeof(short));
		/* test if number of strings isn't too large ? */
		i = mem_block[A_STRINGS].current_size / sizeof str - 1;
	}
	PROG_STRING(i) = str;
	((short *) mem_block[A_STRING_NEXT].block)[i] = next;
	((short *) mem_block[A_STRING_REFS].block)[i] = 1;
	*idxp = i;
	return i;
}

void free_prog_string(short num)
{
	short i, prv, *next_tab, top, *idxp;
	char **p, *str;
	unsigned char hash, mask;

	top = mem_block[A_STRINGS].current_size / sizeof(char *) - 1;
	if (num < 0 || num > top) {
		yyerror("free_prog_string: index out of range.\n");
		return;
	}
	if (--((short *) mem_block[A_STRING_REFS].block)[num] >= 1)
		return;

	p = (char **) mem_block[A_STRINGS].block;
	next_tab = (short *) mem_block[A_STRING_NEXT].block;

	str = p[num];
	STRING_HASH(hash, str);
	idxp = &string_idx[hash];

	for (prv = -1, i = *idxp; i != num; prv = i, i = next_tab[i]) {
		if (i == -1) {
			yyerror("free_prog_string: string not in prog table.\n");
			return;
		}
	}

	if (prv == -1) {		/* string is head of list */
		*idxp = next_tab[i];
		if (*idxp == -1) {
			/* clear tag bit since hash chain now empty */
			mask = 1 << (hash & 7);
			string_tags[hash >> 3] &= ~mask;
		}
	} else {			/* easy unlink */
		next_tab[prv] = next_tab[i];
	}

	free_string(str);		/* important */
	p[i] = 0;
	if (i != top) {
		if (i < freed_string || freed_string == -1)
			freed_string = i;
	} else {
		/* shrink table */
		mem_block[A_STRINGS].current_size -= sizeof str;
		mem_block[A_STRING_REFS].current_size -= sizeof(short);
		mem_block[A_STRING_NEXT].current_size -= sizeof(short);
	}
}

int validate_function_call(int f, parse_node_t *args)
{
	function_t *funp = FUNCTION_DEF(f);
	int num_arg = ( args ? args->kind : 0 );
	int num_var = 0;
	parse_node_t *pn = args;
	unsigned short *arg_types = 0;

	while (pn) {
		if (pn->type & 1) num_var++;
		pn = pn->r.expr;
	}

	/*
	 * Check number of arguments.
	 */
	char buff[256];
	char *end = EndOf(buff);
	char *p;

	if (num_var) {
		p = strput(buff, end, "Illegal to pass a variable number of arguments to non-varargs function ");
		p = strput(p, end, funp->name);
		p = strput(p, end, "\n");
		yyerror(buff);
	} else if (funp->num_arg != num_arg) {
		p = strput(buff, end, "Wrong number of arguments to ");
		p = strput(p, end, funp->name);
		p = strput(p, end, "\n    Expected: ");
		p = strput_int(p, end, funp->num_arg);
		p = strput(p, end, "  Got: ");
		p = strput_int(p, end, num_arg);
		yyerror(buff);
	}

	/*
	 * Check the argument types.
	 */
	int which = FUNCTION_TEMP(f)->u.index;
	int start = *((unsigned short *)mem_block[A_ARGUMENT_INDEX].block + which);
	if (start != INDEX_START_NONE)
		arg_types = (unsigned short *)mem_block[A_ARGUMENT_TYPES].block + start;

	if (arg_types) {
		int arg, i, tmp;
		parse_node_t *enode = args;
		int fnarg = funp->num_arg;

		for (i = 0; (unsigned) i < fnarg && i < num_arg; i++) {
			if (enode->type & 1) break;
			tmp = enode->v.expr->type;

			arg = arg_types[i];

			if (!compatible_types(tmp, arg)) {
				char buff[256];
				char *end = EndOf(buff);
				char *p;

				p = strput(buff, end, "Bad type for argument ");
				p = strput_int(p, end, i+1);
				p = strput(p, end, " of ");
				p = strput(p, end, funp->name);
				p = strput(p, end, " ");
				p = get_two_types(p, end, arg_types[i], tmp);
				yyerror(buff);
			}
			enode = enode->r.expr;
		}
	}
	return funp->type;
}

parse_node_t * validate_efun_call(int f, parse_node_t *args) {
	int num = args->v.number;
	int min_arg, max_arg, def, *argp;
	int num_var = 0;
	parse_node_t *pn = args->r.expr;

	while (pn) {
		if (pn->type & 1) num_var++;
		pn = pn->r.expr;
	}

	if (f != -1) {
		min_arg = predefs[f].min_args;
		max_arg = predefs[f].max_args;

		def = predefs[f].Default;
		if (!num_var && def != DEFAULT_NONE && num == min_arg -1) {
			parse_node_t *tmp;
			tmp = new_node_no_line();
			tmp->r.expr = 0;
			tmp->type = 0;
			args->l.expr->r.expr = tmp;
			if (def == DEFAULT_THIS_OBJECT) {
				tmp->v.expr = new_node_no_line();
				tmp->v.expr->kind = NODE_EFUN;
				tmp->v.expr->v.number = predefs[this_efun].token;
				tmp->v.expr->l.number = 0;
				tmp->v.expr->type = TYPE_ANY;
				tmp->v.expr->r.expr = 0;
			} else {
				CREATE_NUMBER(tmp->v.expr, def);
			}
			args->v.number++;
			num++;
		} else if (num_var && max_arg != -1) {
			char buff[256];
			char *end = EndOf(buff);
			char *p;

			p = strput(buff, end, "Illegal to pass variable number of arguments to non-varargs efun ");
			p = strput(p, end, predefs[f].word);
			yyerror(buff);
			CREATE_ERROR(args);
			return args;
		} else if ((num - num_var) < min_arg) {
			char buff[256];
			char *end = EndOf(buff);
			char *p;

			p = strput(buff, end, "Too few arguments to ");
			p = strput(p, end, predefs[f].word);
			CREATE_ERROR(args);
			yyerror(buff);
			return args;
		} else if (num > max_arg && max_arg != -1) {
			char buff[256];
			char *end = EndOf(buff);
			char *p;

			p = strput(buff, end, "Too many arguments to ");
			p = strput(p, end, predefs[f].word);
			yyerror(buff);
			CREATE_ERROR(args);
			return args;
		}
		if (max_arg != -1) {
			/*
			 * Now check all types of arguments to efuns.
			 */
			int i, argn, tmp;
			char buff[256];
			char *end = EndOf(buff);
			char *p;
			parse_node_t *enode = args;
			argp = &efun_arg_types[predefs[f].arg_index];

			for (argn = 0; argn < num; argn++) {
				enode = enode->r.expr;
				if (enode->type & 1) break;
				/* this can happen for default args */
				if (!enode->v.expr) break;
				tmp = enode->v.expr->type;
				for (i=0; !compatible_types(tmp, argp[i]) && argp[i] != 0; i++)
					;

				if (argp[i] == 0) {
					p = strput(buff, end, "Bad argument ");
					p = strput_int(p, end, argn + 1);
					p = strput(p, end, " to efun ");
					p = strput(p, end, predefs[f].word);
					p = strput(p, end, "()");
					yyerror(buff);
				} else {
					/* check for (int) -> (float) promotion */
					if (tmp == TYPE_NUMBER && argp[i] == TYPE_REAL) {
						for (i++; argp[i] && argp[i] != TYPE_NUMBER; i++)
							;
						if (!argp[i])
							enode->v.expr = promote_to_float(enode->v.expr);
					}
					else if (tmp == TYPE_REAL && argp[i] == TYPE_NUMBER) {
						for (i++; argp[i] && argp[i] != TYPE_REAL; i++)
							;
						if (!argp[i])
							enode->v.expr = promote_to_int(enode->v.expr);
					}
				}
				while (argp[i] != 0)
					i++;
				argp += i + 1;
			}
		}
		args->l.number = num;
		args->v.number = predefs[f].token;
		args->type = predefs[f].ret_type;
		if (args->type == TYPE_NOVALUE) {
			args->v.number += NOVALUE_USED_FLAG;
			args->type = TYPE_VOID;
		}
		args->kind = NODE_EFUN;
	} else {
		CREATE_ERROR(args);
	}
	return args;
}

parse_node_t * promote_to_float(parse_node_t *node) {
	parse_node_t *expr;
	if (node->kind == NODE_NUMBER) {
		node->kind = NODE_REAL;
		node->v.real = node->v.number;
		return node;
	}
	expr = new_node();
	expr->kind = NODE_EFUN;
	expr->v.number = predefs[to_float_efun].token;
	expr->type = TYPE_REAL;
	expr->l.number = 1;
	expr->r.expr = new_node_no_line();
	expr->r.expr->kind = 1;
	expr->r.expr->l.expr = expr->r.expr;
	expr->r.expr->type = 0;
	expr->r.expr->v.expr = node;
	expr->r.expr->r.expr = 0;
	return expr;
}

parse_node_t * promote_to_int(parse_node_t *node) {
	parse_node_t *expr;
	if (node->kind == NODE_REAL) {
		node->kind = NODE_NUMBER;
		node->v.number = node->v.real;
		return node;
	}
	expr = new_node();
	expr->kind = NODE_EFUN;
	expr->v.number = predefs[to_int_efun].token;
	expr->type = TYPE_NUMBER;
	expr->l.number = 1;
	expr->r.expr = new_node_no_line();
	expr->r.expr->kind = 1;
	expr->r.expr->l.expr = expr->r.expr;
	expr->r.expr->type = 0;
	expr->r.expr->v.expr = node;
	expr->r.expr->r.expr = 0;
	return expr;
}

parse_node_t *do_promotions(parse_node_t *node, int type) {
	if (type == TYPE_REAL) {
		if (node->type == TYPE_NUMBER || node->kind == NODE_NUMBER)
			return promote_to_float(node);
	}
	if (type == TYPE_NUMBER && node->type == TYPE_REAL)
		return promote_to_int(node);
	return node;
}

void yywarn(const char *msg, ...)
{
	va_list ap;

	num_parse_warning++;

	fprintf(stderr, "yywarn: ");
	va_start(ap, msg);
	vfprintf(stderr, msg, ap);
	va_end(ap);
	fprintf(stderr, "\n");
}

void yyerror(const char *msg, ...)
{
	va_list ap;

	num_parse_error++;

	fprintf(stderr, "yyerror: ");
	va_start(ap, msg);
	vfprintf(stderr, msg, ap);
	va_end(ap);
	fprintf(stderr, "\n");
}

/*
 * Initialize the environment that the compiler needs.
 */
static void prolog(int f, char *name)
{
	int i;

	num_parse_warning = 0;
	num_parse_error = 0;
	var_defined = 0;

	/* Initialize memory blocks where the result of the compilation
	 * will be stored.
	 */
	for (i=0; i < NUMAREAS; i++) {
		mem_block[i].block = malloc(START_BLOCK_SIZE);
		mem_block[i].current_size = 0;
		mem_block[i].max_size = START_BLOCK_SIZE;
	}
	for (i = 0; i < NUMTREES; i++) {
		comp_trees[i] = 0;
	}
	prog_flags = 0;
	func_index_map = 0;
	comp_def_index_map = 0;

	memset(string_tags, 0, sizeof(string_tags));
	freed_string = -1;
	i_initialize_parser();

	current_file = make_shared_string(name);

	start_new_file(f);
}

/*
 * The program has errors, clean things up.
 */
static void clean_parser()
{
	int i, n;
	function_t *funp;

	/*
	 * Free function stuff.
	 */
	for (i = 0; i < mem_block[A_FUNCTIONS].current_size / sizeof(function_t); i++) {
		funp = FUNC(i);
		if (funp->name) free_string(funp->name);
	}

	n = mem_block[A_STRINGS].current_size / sizeof(char *);
	for (i = 0; i < n; i++) {
		free_string(*((char **)mem_block[A_STRINGS].block + i));
	}
	n = mem_block[A_VAR_NAME].current_size / sizeof(char *);
	for (i = 0; i < n; i++) {
		free_string(*((char **)mem_block[A_VAR_NAME].block + i));
	}

	prog = 0;
	for (i=0; i<NUMAREAS; i++)
		free(mem_block[i].block);
	if (comp_def_index_map) free((char *) comp_def_index_map);
	if (func_index_map) free((char *) func_index_map);
	if (prog_flags) free((char *) prog_flags);

	/* don't need the parse trees any more */
	release_tree();
	i_uninitialize_parser();
	clean_up_locals();
	scratch_destroy();
	free_unused_identifiers();
}

static inline void copy_in(int which, char **start) {
	char *block;
	int size;

	size = mem_block[which].current_size;
	if (!size) return;

	block = mem_block[which].block;
	memcpy(*start, block, size);

	*start += align(size);
}

static int compare_funcs(unsigned short *x, unsigned short *y) {
	char *n1 = FUNC(*x)->name;
	char *n2 = FUNC(*y)->name;
	int sp1, sp2;

	/* make sure #global_init# stays last; also shuffle empty entries to
	 * the end so we can delete them easily.
	 */
	if (n1[0] == '#')
		sp1 = 1;
	else if (FUNC(*x)->address == ADDRESS_MAX)
		sp1 = 2;
	else
		sp1 = 0;

	if (n2[0] == '#')
		sp2 = 1;
	else if (FUNC(*y)->address == ADDRESS_MAX)
		sp2 = 2;
	else
		sp2 = 0;

	if (sp1 < sp2)
		return -1;
	if (sp1 > sp2)
		return 1;

	if (n1 < n2)
		return -1;
	if (n1 > n2)
		return 1;

	return 0;
}

static void handle_functions()
{
	int num_func, total_func;
	int i;
	compiler_temp_t *cur_def;
	int num_def;
	int final_index;

	/* Pass one: Sort the compiler functions first                        */

	num_func = total_func = mem_block[A_FUNCTIONS].current_size / sizeof (function_t);
	if (num_func) {
		func_index_map = CALLOCATE(num_func, unsigned short);
		comp_sorted_funcs = CALLOCATE(num_func, unsigned short);

		i = num_func;
		while (i--) func_index_map[i] = i;

		quickSort(func_index_map, num_func, sizeof(unsigned short),
				compare_funcs);

		i = num_func;
		while (i--)
			comp_sorted_funcs[func_index_map[i]] = i;

		while (num_func &&
				FUNC(func_index_map[num_func-1])->address == ADDRESS_MAX)
			num_func--;
	}

	/* Pass one: We allocate space for the comp_def_index_map             */
	/*           and prog_flags, and fill them out in sequential order    */

	num_def = mem_block[A_FUNCTION_DEFS].current_size/sizeof(compiler_temp_t);
	if (num_def) {
		comp_def_index_map = CALLOCATE(num_def, unsigned short);
		prog_flags = CALLOCATE(total_func, unsigned short);

		for (i = 0; i < num_def; i++) {
			cur_def = FUNCTION_TEMP(i);

			final_index = comp_sorted_funcs[cur_def->u.index];

			comp_def_index_map[i] = final_index;
			prog_flags[final_index] = cur_def->flags;
		}
	}

	if (total_func)
		free((char *) comp_sorted_funcs);
}

/*
 * The program has been compiled. Prepare a 'program_t' to be returned.
 */
static program_t *epilog(void)
{
	int size, i, lnsz, lnoff;
	char *p;
	int num_func;
	program_t *prog;

	if (num_parse_error > 0) {
		/* don't print these; they can be wrong, since we didn't parse the
		   entire file */
		clean_parser();
		end_new_file();
		free_string(current_file);
		current_file = 0;
		return 0;
	}

	/*
	 * Define the #global_init# function, but only if there was any code
	 * to initialize.
	 */
	UPDATE_PROGRAM_SIZE;

	if (comp_trees[TREE_INIT]) {
		parse_node_t *pn, *newnode;
		int fun;
		/* end the __INIT function */
		CREATE_RETURN(pn, 0);
		newnode = comp_trees[TREE_INIT];
		CREATE_TWO_VALUES(comp_trees[TREE_INIT], 0,
				newnode, pn);
		fun = define_new_function(APPLY___INIT, 0, 0,
				0, TYPE_VOID);
		pn = new_node_no_line();
		pn->kind = NODE_FUNCTION;
		pn->v.number = fun;
		pn->l.number = 0;
		pn->r.expr = comp_trees[TREE_INIT];
		comp_trees[TREE_INIT] = pn;
	}

	/* Do stuff with functions first */
	handle_functions();

	/* generate the trees */
	/* TODO
	current_tree = TREE_MAIN;
	generate(comp_trees[TREE_MAIN]);

	current_tree = TREE_INIT;
	generate(comp_trees[TREE_INIT]);

	current_tree = TREE_MAIN;

	generate_final_program(0);
	*/
	UPDATE_PROGRAM_SIZE;

	if (mem_block[0].current_size > ADDRESS_MAX) {
		yyerror("Program too large");
		clean_parser();
		end_new_file();
		free_string(current_file);
		current_file = 0;
		return 0;
	}

	// TODO: generate_final_program(1);

	free_tree();

	size = align(sizeof (program_t));

	for (i=0; i<NUMPAREAS; i++)
		if (i != A_LINENUMBERS && i != A_FILE_INFO &&
				i != A_FUNCTION_DEFS && i != A_FUNCTIONS &&
				i != A_ARGUMENT_INDEX) {
			size += align(mem_block[i].current_size);
		}

	num_func = mem_block[A_FUNCTIONS].current_size/sizeof(function_t);

	while (num_func &&
			FUNC(func_index_map[num_func - 1])->address == ADDRESS_MAX)
		num_func--;

	size += align((num_func * sizeof(function_t)));   /* A_FUNCTIONS */

	/* function flags */
	size += align((num_func * sizeof(unsigned short)));

	/* A_ARGUMENT_INDEX */
	if (mem_block[A_ARGUMENT_INDEX].current_size)
		size += align(num_func * sizeof(unsigned short));

	p = (char *)malloc(size);
	prog = (program_t *)p;
	*prog = NULL_program;
	prog->total_size = size;
	prog->ref = 0;
	prog->func_ref = 0;
	prog->name = current_file;

	current_file = 0;

	total_num_prog_blocks++;
	total_prog_block_size += size;

	prog->line_swap_index = -1;

	/* Format is now:
	 * <short total size> <short line_info_offset> <file info> <line info>
	 */
	lnoff = 2 + (mem_block[A_FILE_INFO].current_size / sizeof(short));
	lnsz = lnoff * sizeof(short) + mem_block[A_LINENUMBERS].current_size;

	prog->file_info = (unsigned short *) malloc(lnsz);

	prog->file_info[0] = (unsigned short)lnsz;
	prog->file_info[1] = (unsigned short)lnoff;

	memcpy(((char*)&prog->file_info[2]),
			mem_block[A_FILE_INFO].block,
			mem_block[A_FILE_INFO].current_size);

	prog->line_info = (unsigned char *)(&prog->file_info[lnoff]);
	memcpy(((char*)&prog->file_info[lnoff]),
			mem_block[A_LINENUMBERS].block,
			mem_block[A_LINENUMBERS].current_size);

	p += align(sizeof(program_t));
	prog->program = p;
	prog->program_size = mem_block[A_PROGRAM].current_size;
	copy_in(A_PROGRAM, &p);


	/* copy the functions in */
	prog->num_functions_defined = num_func;

	prog->function_table = (function_t *)p;
	for (i = 0; i < num_func; i++)
		prog->function_table[i] = *FUNC(func_index_map[i]);

	p += align(sizeof(function_t) * num_func);


	prog->function_flags = (unsigned short *)p;
	if (prog_flags) {
		memcpy(p,prog_flags, num_func * sizeof(unsigned short));
		free((char *)prog_flags);
	}
	p += align(num_func * sizeof(unsigned short));

	if (mem_block[A_ARGUMENT_INDEX].current_size) {
		unsigned short *dest;

		prog->argument_types = (unsigned short *)p;
		copy_in(A_ARGUMENT_TYPES, &p);

		dest = prog->type_start = (unsigned short *)p;
		i = num_func;
		while (i--)
			dest[i] = *((unsigned short *) mem_block[A_ARGUMENT_INDEX].block
					+ func_index_map[i]);
		p += align(num_func * sizeof(unsigned short));
	} else {
		prog->argument_types = 0;
		prog->type_start = 0;
	}

	prog->strings = (char **)p;
	prog->num_strings = mem_block[A_STRINGS].current_size /
		sizeof (char *);
	copy_in(A_STRINGS, &p);

	prog->num_variables_defined = mem_block[A_VAR_NAME].current_size /
		sizeof (char *);
	prog->num_variables_total = mem_block[A_VAR_TEMP].current_size /
		sizeof (variable_t);

	prog->variable_table = (char **)p;
	copy_in(A_VAR_NAME, &p);
	prog->variable_types = (unsigned short *)p;
	copy_in(A_VAR_TYPE, &p);

#ifdef DEBUG
	if (p - (char *)prog != size) {
		fprintf(stderr, "Program size miscalculated for /%s.\n", prog->name);
		fprintf(stderr, "is: %i, expected: %i\n", p-(char *)prog, (int) size);
	}
#endif
#ifdef BINARIES
	/* TODO:
	   if ((pragmas & PRAGMA_SAVE_BINARY)) {
	   save_binary(prog, &mem_block[A_INCLUDES], &mem_block[A_PATCH]);
	   }
	 */
#endif

	// TODO: swap_line_numbers(prog); /* do this after saving binary */

	for (i=0; i<NUMAREAS; i++)
		free((char *)mem_block[i].block);
	if (comp_def_index_map) free((char *) comp_def_index_map);
	if (func_index_map) free((char *) func_index_map);

	/*  marion
	    Do referencing here - avoid multiple referencing when an object
	    inherits more than one object and one of the inherited is already
	    loaded and not the last inherited
	 */
	reference_prog(prog, "epilog");

	release_tree();
	i_uninitialize_parser();
	scratch_destroy();
	clean_up_locals();
	free_unused_identifiers();
	end_new_file();

	return prog;
}

/*
 * Compile an LPC file.
 */
program_t * compile_file(int f, char *name)
{
	int yyparse(void);
	static int guard = 0;
	program_t *prog;

	/* The parser isn't reentrant.  On a few occasions (compile
	 * errors, valid_override) LPC code is called during compilation,
	 * causing the possibility of arriving here again.
	 */
	if (guard) {
		/* TODO: error("Object cannot be loaded during compilation.\n"); */
	}
	guard = 1;

	prolog(f, name);
	yyparse();
	prog = epilog();

	guard = 0;
	return prog;
}
